home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 1 / Cream of the Crop 1.iso / UTILITY / PRN2FIL3.ARJ / PRN2FILE.ASM next >
Assembly Source File  |  1992-02-11  |  36KB  |  896 lines

  1. ;----------------------------------------------------------------------
  2. ; PRN2FILE.ASM - A resident program which redirects printer output.
  3. ; SYNTAX: PRN2FILE d:path:filename.ext [/Pn] [/Bn] [/U]
  4. ;  1)  Run PRN2FILE with the desired filename to activate it.
  5. ;  2)  Run it again with no filename to turn off redirection.
  6. ;  3)  Run it with a differant filename to change destination file.
  7. ;  4)  Use /P to designate the printer number (defaults to 1)
  8. ;  5)  Use /B to enter buffer size in K bytes (defaults to 4)
  9. ;  6)  Use /F to print just to file and not to printer (default is both)
  10. ;  6)  Use /A to append to file (default is to create new file)
  11. ;  7)  Use /U to uninstall the program
  12. ;----------------------------------------------------------------------
  13. CSEG            SEGMENT
  14.         ASSUME  CS:CSEG,DS:NOTHING
  15.  
  16.         ORG     100H    ;Beginning for .COM programs
  17. START:          JMP INITIALIZE  ;Initialization code is at end
  18.  
  19. ;----------------------------------------------------------------------
  20. ; Data area used by this program
  21. ;----------------------------------------------------------------------
  22. COPYRIGHT       DB      " PRN2FILE 1.1 (c) 1987 Ziff Communications Co.",0DH,0AH
  23.         DB      "Modifications (c) 1991 Automated Answers$",1AH
  24. PROGRAMMERS     DB      "Tom Kihlken"
  25.         DB      "Russell Cummings"
  26. REDIRECT_MESS   DB      "LPT"
  27. PRN_NUM         DB      "1 Redirected to: $"
  28. BAD_FILENAME    DB      "Invalid filename.$"
  29. BAD_PARAM       DB      "Usage: PRN2FILE [path][filename][/Pn][/Bnn][/F][/A][/U]$"
  30. BAD_ALLOC       DB      "Memory Allocation Error.$"
  31. BAD_UNINSTALL   DB      "Cannot Uninstall.$"
  32. PRN_TXT         DB      "PRN$"
  33. CRLF            DB      13,10,"$"
  34. ERR_MESSAGE     DB      13,10,"*Buffer Overflow*",13,10
  35. MESS_LENGTH     EQU     $ - OFFSET ERR_MESSAGE
  36. freq1           dw      400     ;Frequency of first sound
  37. freq2           dw      1300    ;Frequency of second sound
  38. lgth1           dw      100     ;Duration of first sound
  39. lgth2           dw      40      ;Duration between sounds
  40. lgth3           dw      100     ;Duration of second sound
  41. bell_flag       db      1       ;Indicates whether sound to be made
  42. OLDINT08        DD      ?       ;Old timer tick interrupt vector
  43. OLDINT17        DD      ?       ;Old printer output vector
  44. OLDINT21        DD      ?       ;Old dos function interrupt vector
  45. OLDINT28        DD      ?       ;Old dos waiting interrupt vector
  46. DOS_FLAG        DD      ?       ;Dos busy flag
  47. JUST_FILE       DB      0       ;Just file flag
  48. APPEND_FILE     DB      0       ;Append file flag
  49. SWITCH          DB      0       ;On/off switch for redirecting printer
  50. TIMEOUT         DW      0       ;Holds timeout counter to flush buffer
  51. INSTALLED_SEG   DW      0       ;Segment location of installed copy
  52. WRITE_FLAG      DB      0       ;Indicates buffer should be written
  53. PRINTER_NUM     DW      0       ;Default to first parallel printer
  54. BUFF_POINTER    DW      0       ;Pointer to next space in buffer
  55. BUFF_SIZE       DW      4       ;Size of buffer
  56. BUFF_SEGMENT    DW      0       ;Segment address of buffer
  57. TIME_TO_WRITE   EQU     400H    ;Flush buffer when this full
  58.  
  59. ;-----------------------------------------------------------------------
  60. ; Interrupt 17 routine. (BIOS printer output)
  61. ; If output is to the selected printer and switch is on then redirect
  62. ; the character into a file.
  63. ;-----------------------------------------------------------------------
  64. NEWINT17        PROC    FAR
  65.         ASSUME  DS:NOTHING, ES:NOTHING
  66.  
  67.         CMP     DX,CS:PRINTER_NUM ;Is this the selected printer?
  68.         JNE     IGNORE          ;If not, let bios handle it
  69.         CMP     CS:SWITCH,1     ;Is redirection turned on?
  70.         JE      REDIRECT_IT     ;If on, take jump
  71. IGNORE:
  72.         JMP     CS:OLDINT17     ;Jump to the bios routine
  73. REDIRECT_IT:
  74.                 CMP  CS:JUST_FILE,1     ;Just file turned on?
  75.                 JE   NO_PUSHES          ;If on, take jump
  76.  
  77.         PUSH ES                 ;Save registers, to be popped
  78.         PUSH DS                 ;off later to go to old int 17
  79.         PUSH SI
  80.         PUSH DI
  81.         PUSH DX
  82.         PUSH CX
  83.         PUSH BX
  84.         PUSH AX
  85. NO_PUSHES:
  86.         STI                     ;Get interrupts back on
  87.  
  88. ;*****************************************************************************
  89. ;* Below is the key modification by Mel Brown to the original PRN2FILE.      *
  90. ;* It adds a call to a sound-making routine when appropriate.  The sound     *
  91. ;* routines are from the book "Bluebook Of Assembly Routines For The IBM PC" *
  92. ;* by Christopher L. Morgan.                                                 *
  93. ;* Also replaced some erroneous JCXZ instructions to allow the program to    *
  94. ;* accept single-character filenames.                                        *
  95. ;*****************************************************************************
  96.  
  97. ;Check BELL_FLAG to determine whether 5 seconds have elapsed since most
  98. ;recent write to file.  If so, make a beep-beep sound to remind user that
  99. ;PRN2FILE is intercepting printer traffic and redirecting it to a disk file.
  100.  
  101.                 cmp     cs:bell_flag,1  ;Check flag
  102.                 jne     no_beep_beep    ;If not set, bypass next
  103.                 call    beep_beep       ;Do the sound
  104.                 mov     cs:bell_flag,0  ;Reset the flag
  105. no_beep_beep:
  106.                 MOV     CS:TIMEOUT,91   ;Reset timeout counter
  107.         PUSH    SI              ;Si will be used for a pointer
  108.         CMP     AH,1            ;Initializing the printer?
  109.         JE      WRITE_BUFF      ;If yes, then flush the buffer
  110.         OR      AH,AH           ;Printing a character?
  111.         JNZ     PRINT_RET       ;If not, take jump to return
  112.         MOV     SI,CS:BUFF_POINTER      ;Get pointer to the buffer
  113.         CMP     SI,CS:BUFF_SIZE ;Is buffer filled up yet?
  114.         JE      PRINT_RET       ;If full just return.
  115.  
  116.         PUSH    DS              ;Save the data segment
  117.         MOV     DS,CS:BUFF_SEGMENT      ;Load DS with the buffer seg
  118.         MOV     DS:[SI],AL      ;Store the character in buffer
  119.         POP     DS              ;Restore data segment
  120.         INC     SI              ;And point to next position
  121.         MOV     CS:BUFF_POINTER,SI      ;Save the new pointer
  122.  
  123.         CMP     SI,TIME_TO_WRITE ;Is buffer filling up yet?
  124.         JL      PRINT_RET        ;If not, just return
  125. WRITE_BUFF:
  126.         MOV     CS:WRITE_FLAG,1 ;Signal buffer needs emptying
  127.         PUSH    DS
  128.         PUSH    BX
  129.         LDS     BX,CS:DOS_FLAG  ;Get location of dos flag
  130.         CMP     BYTE PTR [BX],0 ;Is dos busy flag set?
  131.         POP     BX
  132.         POP     DS
  133.         JNE     PRINT_RET       ;If busy, do nothing
  134.         CALL    WRITE_TO_FILE   ;This empties the buffer
  135. PRINT_RET:
  136.         POP     SI
  137.         MOV     AH,10010000B    ;Return printer status good
  138.  
  139.                 CMP  CS:JUST_FILE,1     ;Just file turned on?
  140.                 JE   NO_POPS            ;If on, take jump
  141.         
  142.         POP AX                
  143.         POP BX
  144.         POP CX
  145.         POP DX
  146.         POP DI
  147.         POP SI
  148.         POP DS
  149.         POP ES                  ;Restore registers and
  150.  
  151.         JMP CS:OLDINT17         ;print onto printer
  152. NO_POPS:  
  153.                 IRET                    ;Return from interrupt        
  154. NEWINT17        ENDP                    ;Without going to printer
  155.  
  156. ;----------------------------------------------------------------------
  157. ; New interrupt 08h (timer tick) decrement the timeout counter. Set
  158. ; the flush flag when counter reaches zero.
  159. ;----------------------------------------------------------------------
  160. NEWINT08        PROC    FAR
  161.         ASSUME  DS:NOTHING, ES:NOTHING
  162.  
  163.         PUSHF                   ;Simulate an interrupt
  164.         CALL    CS:OLDINT08     ;Do normal timer routine
  165.         DEC     CS:TIMEOUT      ;Count down the flush time count
  166.         JNZ     STILL_TIME      ;Count until it gets to zero
  167.         CMP     CS:BUFF_POINTER,0 ;Anything in buffer?
  168.         JE      STILL_TIME      ;If not, just continue
  169.         MOV     CS:WRITE_FLAG,1 ;Set flush trigger
  170. STILL_TIME:
  171.         IRET                    ;Return from timer interrupt
  172.  
  173. NEWINT08        ENDP
  174.  
  175. ;----------------------------------------------------------------------
  176. ; Interrupt 21 routine.  (DOS function calls) intercept function 40h
  177. ; when it writes to the printer.  Also check to see if WRITE_FLAG is
  178. ; set to one.  If it is then flush the buffer.
  179. ;----------------------------------------------------------------------
  180. NEWINT21        PROC    FAR
  181.         ASSUME  DS:NOTHING, ES:NOTHING
  182.  
  183.         PUSHF                   ;Save the callers flags
  184.         CMP     CS:WRITE_FLAG,1 ;Buffer need to be written?
  185.         JNE     DONT_WRITE      ;If not, then just return
  186.         PUSH    DS
  187.         PUSH    BX
  188.         LDS     BX,CS:DOS_FLAG  ;Get location of DOS flag
  189.         CMP     BYTE PTR [BX],0 ;Is DOS busy flag set?
  190.         POP     BX
  191.         POP     DS
  192.         JNE     DONT_WRITE      ;If busy, do nothing
  193.         CALL    WRITE_TO_FILE   ;Empty the buffer now
  194. DONT_WRITE:
  195.         OR      AH,AH           ;Doing function zero?
  196.         JNE     NOT_ZERO
  197.         MOV     AX,4C00H        ;If yes, change it to 4Ch
  198. NOT_ZERO:
  199.         CMP     AH,40H          ;Writing to a device?
  200.         JNE     NOT_PRINTER     ;If not, just continue
  201.         CMP     BX,4            ;Writing to the printer handle?
  202.         JNE     NOT_PRINTER     ;If not, just continue
  203.         CMP     CS:SWITCH,1     ;Is redirection on?
  204.         JE      PRINT_IT        ;If yes, then redirect it
  205. NOT_PRINTER:
  206.         POPF                    ;Recover flags from stack
  207.         CLI
  208.         JMP     CS:OLDINT21     ;Do the DOS function
  209.  
  210. ; Emulate print string function by involking INT 17h
  211.  
  212. PRINT_IT:
  213.         STI                     ;Reenable interrupts
  214.         CLD                     ;String moves forward
  215.  
  216.         PUSH    CX              ;Save these registers
  217.         PUSH    DX
  218.         PUSH    SI
  219.  
  220.         MOV     SI,DX           ;Get pointer to string
  221.         MOV     DX,PRINTER_NUM  ;Selected printer ID in DX
  222.         JCXZ    END_LOOP        ;Skip loop if count is zero
  223. PRINT_LOOP:
  224.         LODSB                   ;Load next character from string
  225.         MOV     AH,00           ;Print character function
  226.         INT     17H             ;BIOS print
  227.         LOOP    PRINT_LOOP      ;Loop through whole string
  228. END_LOOP:
  229.         POP     SI
  230.         POP     DX
  231.         POP     CX
  232.  
  233.         MOV     AX,CX           ;All bytes were output
  234.         POPF                    ;Restore the callers flags
  235.  
  236.         CLC                     ;Return success status
  237.         STI                     ;Reenable interrupts
  238.         RET     2               ;Return with current flags
  239.  
  240. NEWINT21        ENDP
  241.  
  242. ;----------------------------------------------------------------------
  243. ; This copies the buffer contents to a file. It should only be called
  244. ; when dos is in a reentrant condition.  All registers are preserved
  245. ;----------------------------------------------------------------------
  246. WRITE_TO_FILE   PROC    NEAR
  247.         ASSUME  DS:NOTHING, ES:NOTHING
  248.  
  249.         PUSH    AX              ;Save registers we need to use
  250.         PUSH    BX
  251.         PUSH    CX
  252.         PUSH    DX
  253.         PUSH    DS
  254.         PUSH    ES
  255.  
  256.         PUSH    CS
  257.         POP     DS              ;Set DS to code segment
  258.         ASSUME  DS:CSEG         ;Tell assembler DS is CSEG
  259.         MOV     WRITE_FLAG,0    ;Clear write request flag
  260.         MOV     AX,3524H        ;Get dos critical error vector
  261.         CALL    DOS_FUNCTION    ;Do the dos function
  262.         PUSH    BX              ;Save old vector on stack
  263.         PUSH    ES
  264.  
  265. ; Replace the dos severe error interrupt with our own routine.
  266.  
  267.         MOV     DX,OFFSET NEWINT24
  268.         MOV     AX,2524H        ;Setup to change int 24h vector
  269.         CALL    DOS_FUNCTION    ;Do the dos function
  270.  
  271. ; First try to open the file.  If dos returns with the carry flag set,
  272. ; the file didn't exist and we must create it.  Once the file is opened,
  273. ; advance the file pointer to the end of file to append.
  274.  
  275.         CMP     BUFF_POINTER,0  ;Anything in the buffer?
  276.         JE      REP_VECTOR      ;If not, no nothing
  277.         MOV     DX,OFFSET FILENAME ;Point to filename
  278.         MOV     AX,3D02H        ;Dos function to open file
  279.         CALL    DOS_FUNCTION    ;Do the dos function
  280.         JC      FILE_NOT_FOUND  ;Set if file doesn't exist.
  281.         MOV     BX,AX           ;Keep handle in BX also
  282.         XOR     CX,CX           ;Move dos file pointer to the
  283.         XOR     DX,DX           ;End of the file. this lets us
  284.         MOV     AX,4202H        ;Append this to an existing file
  285.         CALL    DOS_FUNCTION    ;Do the dos function
  286.         JC      CLOSE_FILE      ;On any error, take jump
  287.         JMP     SHORT WRITE_FILE
  288. FILE_NOT_FOUND:
  289.         CMP     AX,2            ;Was it file not found error?
  290.         JNE     REP_VECTOR      ;If not, just quit
  291.         MOV     CX,0020H        ;Attribute for new file
  292.         MOV     AH,3CH          ;Create file for writing
  293.         CALL    DOS_FUNCTION    ;Do the dos function
  294.         JC      CLOSE_FILE      ;On any error, take jump
  295.  
  296.         MOV     BX,AX           ;Save handle in BX also
  297. WRITE_FILE:     MOV     DX,0            ;Point to buffer
  298.         MOV     CX,BUFF_POINTER ;Number of chars in buffer
  299.         MOV     AH,40H          ;Dos write to a device function
  300.         PUSH    DS
  301.         MOV     DS,BUFF_SEGMENT ;Point to buffer segment
  302.         CALL    DOS_FUNCTION    ;Do the dos function
  303.         POP     DS
  304.         JC      CLOSE_FILE      ;On any error, take jump
  305.         CMP     CX,AX           ;Was everything written
  306.         JNE     CLOSE_FILE      ;If not, it was an error
  307.         CMP     CX,BUFF_SIZE    ;Was buffer full?
  308.         JNE     CLOSE_FILE      ;If not everything is OK
  309.  
  310.         MOV     DX,OFFSET ERR_MESSAGE ;Insert the error message
  311.         MOV     CX,MESS_LENGTH
  312.         MOV     AH,40H          ;Dos write to file function
  313.         CALL    DOS_FUNCTION    ;Do the dos function
  314. CLOSE_FILE:
  315.         MOV     AH,3EH          ;Dos function to close the file
  316.         CALL    DOS_FUNCTION    ;Do the dos function
  317. REP_VECTOR:
  318.         MOV     BUFF_POINTER,0  ;Indicate buffer is empty
  319.         POP     DS              ;Recover int 24h vector from stack
  320.         POP     DX
  321.         MOV     AX,2524H        ;Restore critical error vector
  322.         CALL    DOS_FUNCTION    ;Do the dos function
  323.         ASSUME  DS:NOTHING
  324.         POP     ES              ;Restore all registers
  325.         POP     DS
  326.         POP     DX
  327.         POP     CX
  328.         POP     BX
  329.         POP     AX
  330.         RET                     ;Finished with writing to disk
  331.  
  332. WRITE_TO_FILE   ENDP
  333.  
  334. ;----------------------------------------------------------------------
  335. ; Make a sound indicating that PRN2FILE is active and redirecting
  336. ; printer output to a disk file
  337. ;----------------------------------------------------------------------
  338. beep_beep       proc    near
  339.                 push    ax
  340.                 push    cx
  341.                 mov     cx,cs:freq1     ;Get first frequency
  342.                 call    freq            ;Convert from frequency to period
  343.                 call    toneset         ;Set the sound frequency
  344.                 call    toneon          ;Start the first sound
  345.                 mov     cx,cs:lgth1     ;Get length of first sound
  346.                 call    delay
  347.                 call    toneoff         ;End the first sound
  348.                 mov     cx,cs:lgth2     ;Get delay between sounds
  349.                 call    delay
  350.                 mov     cx,cs:freq2     ;Get second frequency
  351.                 call    freq            ;Convert from frequency to period
  352.                 call    toneset         ;Set the sound frequency
  353.                 call    toneon          ;Start the second sound
  354.                 mov     cx,cs:lgth3     ;Get length of second sound
  355.                 call    delay
  356.                 call    toneoff         ;End the second sound
  357.                 pop     cx
  358.                 pop     ax
  359.                 ret
  360.  
  361. beep_beep       endp
  362.  
  363. ;----------------------------------------------------------------------
  364. ; Convert from frequency to period
  365. ;----------------------------------------------------------------------
  366. freq            proc    near
  367.                 push    dx
  368.                 push    ax
  369.                 mov     dx,12h          ;Upper part of numerator
  370.                 mov     ax,34deh        ;Lower part of numerator
  371.                 div     cx              ;Divide by frequency
  372.                 mov     cx,ax           ;The quotient is the output
  373.                 pop     ax
  374.                 pop     dx
  375.                 ret
  376.  
  377. freq            endp
  378.  
  379. ;----------------------------------------------------------------------
  380. ;Set the sound frequency into the timer 
  381. ;----------------------------------------------------------------------
  382. toneset         proc    near
  383.                 mov     al,cl           ;Send lower byte
  384.                 out     42h,al          ;to timer
  385.                 mov     al,ch           ;Send upper byte
  386.                 out     42h,al          ;to timer
  387.                 ret
  388.  
  389. toneset         endp
  390.  
  391. ;----------------------------------------------------------------------
  392. ; Turn the sound on
  393. ;----------------------------------------------------------------------
  394. toneon          proc    near
  395.                 push    ax
  396.                 in      al,61h          ;Get contents of port B
  397.                 or      al,3            ;Turn speaker and timer on
  398.                 out     61h,al          ;Send out new values to port B
  399.                 pop     ax
  400.                 ret
  401.  
  402. toneon          endp
  403.  
  404. ;----------------------------------------------------------------------
  405. ; Turn the sound off
  406. ;----------------------------------------------------------------------
  407. toneoff         proc    near
  408.                 push    ax
  409.                 in      al,61h          ;Get contents of port B
  410.                 and     al,0fch         ;Turn speaker and timer off
  411.                 out     61h,al          ;Send out new values to port B
  412.                 pop     ax
  413.                 ret
  414.  
  415. toneoff         endp
  416.  
  417. ;----------------------------------------------------------------------
  418. ; Delay for a specified number of milliseconds
  419. ;----------------------------------------------------------------------
  420. delay           proc    near
  421.                 push    cx
  422. delay1:
  423.                 push    cx              ;Save counter
  424.                 mov     cx,260          ;Timing constant
  425. delay2:
  426.                 loop    delay2          ;Small loop
  427.                 pop     cx              ;Restore counter
  428.                 loop    delay1          ;Loop to count milliseconds
  429.                 pop     cx
  430.                 ret
  431.  
  432. delay           endp
  433.  
  434.  
  435. ;----------------------------------------------------------------------
  436. ; This routine emulates an INT 21 by calling the dos interrupt address
  437. ;----------------------------------------------------------------------
  438. DOS_FUNCTION    PROC    NEAR
  439.         ASSUME  DS:NOTHING, ES:NOTHING
  440.  
  441.         PUSHF                   ;Save the processor flags
  442.         CLI                     ;Clear interrupt enable bit
  443.         CALL    CS:OLDINT21     ;Execute the interupt procedure
  444.         STI                     ;Enable further interrupts
  445.         RET                     ;And return to calling routine
  446.  
  447. DOS_FUNCTION    ENDP
  448.  
  449. ;----------------------------------------------------------------------
  450. ; New interrupt 24h (critical dos error).  This interrupt is only in
  451. ; effect when writing to the disk.  It is required to suppress the
  452. ; 'Abort, Retry, Ignore' message.  All fatal disk errors are ignored.
  453. ;----------------------------------------------------------------------
  454. NEWINT24        PROC    FAR
  455.         ASSUME  DS:NOTHING, ES:NOTHING
  456.  
  457.         STI                     ;Turn interrupts back on
  458.         XOR     AL,AL           ;Tells dos to ignore the error
  459.         MOV     CS:SWITCH,AL    ;Turn off logging of output
  460.         IRET                    ;And return to dos
  461.  
  462. NEWINT24        ENDP
  463.  
  464. ;----------------------------------------------------------------------
  465. ; New interrupt 28h (DOS idle).  Check to see if write_flag is set to
  466. ; one. If it is, then flush the buffer
  467. ;----------------------------------------------------------------------
  468. NEWINT28        PROC    FAR
  469.         ASSUME  DS:NOTHING, ES:NOTHING
  470.  
  471.         STI
  472.         CMP     CS:WRITE_FLAG,0 ;Buffer need to be written?
  473.         JE      DO_NOTHING      ;If not, just continue
  474.         CALL    WRITE_TO_FILE   ;Empty the buffer
  475. DO_NOTHING:
  476.         JMP     CS:OLDINT28     ;Continue with old interrupt
  477.  
  478. NEWINT28        ENDP
  479.  
  480. ;----------------------------------------------------------------------
  481. ; Here is the code used to initialize prn2file.com.  First determine
  482. ; if prn2file is already installed.  If it is, just copy new parameters
  483. ; into the resident programs data area, otherwise save old vectors
  484. ; and replace with new ones.  The output buffer will later overlay
  485. ; this code to conserve memory.
  486. ;----------------------------------------------------------------------
  487.         ASSUME  CS:CSEG, DS:CSEG, ES:NOTHING
  488. INITIALIZE:
  489.         MOV     DX,OFFSET COPYRIGHT
  490.         CALL    STRING_CRLF     ;Display the string
  491.  
  492. ; Search for a previously installed copy of prn2file
  493.  
  494.         NOT     WORD PTR START  ;Modify to avoid false match
  495.         XOR     BX,BX           ;Start search at segment zero
  496.         MOV     AX,CS           ;Compare to this code segment
  497. NEXT_SEGMENT:
  498.         INC     BX              ;Look at next segment
  499.         CMP     AX,BX           ;Until reaching this code seg
  500.         MOV     ES,BX
  501.         JE      NOT_INSTALLED
  502.         MOV     SI,OFFSET START ;Setup to compare strings
  503.         MOV     DI,SI
  504.         MOV     CX,16           ;16 bytes must match
  505.         REP     CMPSB           ;Compare DS:SI to ES:DI
  506.         OR      CX,CX
  507.         JNZ     NEXT_SEGMENT    ;If no match, try next segment
  508.         MOV     ES:SWITCH,1     ;Turn redirection on
  509.         MOV     DX,ES:PRINTER_NUM ;Retrieve old printer number
  510.         MOV     DS:PRINTER_NUM,DX ;Save it here
  511.         MOV     AH,1            ;Initialize the resident copy
  512.         INT     17H             ;To flush it's buffer
  513.         ADD     DL,31H          ;Convert printer num to ascii
  514.         MOV     PRN_NUM,DL      ;Put it into the message area
  515. NOT_INSTALLED:
  516.         MOV     INSTALLED_SEG,ES
  517.         PUSH    CS
  518.         POP     ES              ;Set ES to this segment
  519.         ASSUME  ES:CSEG
  520.         CMP     BYTE PTR DS:[0080],0 ;Anything entered?
  521.         JE      NO_PARAMS       ;If not, take jump
  522. PARSE:
  523.         MOV     AL,"/"          ;Look for a slash
  524.         CALL    LOAD_PARAMS
  525.         REPNE   SCASB           ;Scan for slashes
  526.         JCXZ    ALMOST_PARSE    ;Quit when no more slashes
  527.         MOV     AL,[DI]         ;Get the parameter
  528.         MOV WORD PTR [DI-1],2020H;Erase the slash and letter
  529.         OR      AL,32           ;Convert to lower case
  530.         CMP     AL,"p"          ;Is it the "p" parameter
  531.         JE      SLASH_P
  532.         CMP     AL,"b"          ;Is it the "b" parameter
  533.         JE      SLASH_B
  534.         CMP     AL,"f"          ;Is it the "f" parameter
  535.         JE      SLASH_F
  536.         CMP     AL,"a"          ;Is it the "a" parameter
  537.         JE      SLASH_A
  538.         CMP     AL,"u"          ;Is it the "u" parameter
  539.         JE      SLASH_U
  540. INVALID_PARAM:
  541.         MOV     DX,OFFSET BAD_PARAM ;Point to error message
  542.         JMP     ERR_EXIT
  543. SLASH_U:
  544.         JMP     UNINSTALL       ;Slash "u" means uninstall it
  545. SLASH_B:
  546.         MOV     BUFF_SIZE,0     ;Zero buff size for accumulator
  547. NEXT_DIGIT:
  548.         MOV     AX,BUFF_SIZE    ;Get current buff size
  549.         MOV     BL,10
  550.         MUL     BL              ;Times 10 for next digit
  551.         INC     DI              ;Point to next digit
  552.         MOV     BL,[DI]         ;And get the next one
  553.         SUB     BL,30H          ;Convert it to binary
  554.         JC      PARSE           ;If not a digit, keep parsing
  555.         CMP     BL,9
  556.         JA      PARSE           ;If not a digit, keep parsing
  557.         MOV     BYTE PTR [DI]," ";Erase character from command
  558.         XOR     BH,BH
  559.         ADD     AX,BX           ;Add in this digit
  560.         MOV     BUFF_SIZE,AX    ;And save the new total
  561.         JMP     NEXT_DIGIT
  562. SLASH_P:
  563.         INC     DI              ;Point to the printer number
  564.         MOV     AL,[DI]
  565.         MOV     BYTE PTR [DI]," ";Erase this char from command
  566.         MOV     PRN_NUM,AL      ;Put it in the message area
  567.         SUB     AL,31H          ;Convert it to printer number
  568.         XOR     AH,AH           ;Make it a word
  569.         CMP     AL,3            ;Printer id must be less than 3
  570.         JAE     INVALID_PARAM   ;If it isn't, take jump
  571.         MOV     PRINTER_NUM,AX  ;Store the parameter
  572.         JMP     PARSE           ;Look for more parameters
  573. SLASH_F:
  574.             MOV     AL,1            ;Set just file flag
  575.             MOV     JUST_FILE,AL    ;Put it in the message area
  576.         JMP     PARSE           ;Look for more parameters
  577. SLASH_A:
  578.             MOV     AL,1            ;Set append file flag
  579.             MOV     APPEND_FILE,AL  ;Put it in the message area
  580.         JMP     PARSE           ;Look for more parameters
  581. ALMOST_PARSE:
  582.                 JMP     PARSE_DONE
  583. NO_PARAMS:
  584.         MOV     DX,OFFSET REDIRECT_MESS ;Point to message
  585.         MOV     AH,9            ;Display the string of text
  586.         INT     21H             ;Using DOS display function
  587.         MOV     DX,OFFSET PRN_TXT ;Point to "PRN"
  588.         CALL    STRING_CRLF     ;Display the string
  589.         MOV     AL,0            ;Turn off redirection switch
  590.         JMP     CHECK_FOR_INSTALL
  591. PARSE_DONE:
  592.         CMP     BUFF_SIZE,1     ;Buff must be at least 1K
  593.         JB      INVALID_PARAM   ;If not, exit with error
  594.         CMP     BUFF_SIZE,64    ;Check for maximum buff size
  595.         JA      INVALID_PARAM   ;If above, exit with error
  596.         MOV     AL," "          ;Look for spaces
  597.         CALL    LOAD_PARAMS
  598.         REPE    SCASB           ;Scan for non-space character
  599.         JCXZ    NO_PARAMS       ;Any letters found?
  600.  
  601.         CMP     BYTE PTR [DI],":" ;Was a drive specified?
  602.         JNE     GET_DEF_DRIVE   ;If not, get the default drive
  603.         DEC     DI              ;Now DI points to first letter
  604.         MOV     AL,[DI]         ;Get drive letter in AL
  605.         MOV     WORD PTR [DI],2020H;Erase the drive and colon
  606.         JMP     STORE_DRIVE
  607. GET_DEF_DRIVE:
  608.         MOV     AH,19H          ;Get default drive
  609.         INT     21H
  610.         ADD     AL,65           ;Convert integer drive to ascii
  611. STORE_DRIVE:
  612.         MOV     AH,":"          ;AL has drive, AH has colon
  613.         MOV     WORD PTR FILENAME,AX ;Store drive and colon
  614.         MOV     AL,"\"          ;Look for a backslash
  615.         MOV     FILENAME+2,AL   ;Add a backslash to filename
  616.         CALL    LOAD_PARAMS
  617.         REPNE   SCASB           ;Scan for a backslash
  618.         JCXZ    GET_DEF_PATH    ;If no path, use current path
  619.         MOV     DI,OFFSET FILENAME+2 ;Location to store path
  620.         JMP     STORE_PATH
  621. GET_DEF_PATH:
  622.         MOV     DL,FILENAME     ;Selected drive letter
  623.         AND     DL,11011111B    ;Convert it to upper case
  624.         SUB     DL,64           ;Convert it to integer
  625.         MOV     SI,OFFSET FILENAME + 3 ;Put current path at SI
  626.         MOV     DI,SI           ;Save this for search later
  627.         MOV     AH,47H          ;DOS get current directory
  628.         INT     21H
  629.         JC      BAD_NAME_EXIT   ;Exit if invalid drive
  630.         MOV     AL,0            ;Look for end of path
  631.         CMP     [DI],AL         ;Was there any path?
  632.         JE      STORE_PATH      ;If not, don't scan it
  633.         MOV     CX,64           ;Maximum number of bytes in path
  634.         REPNE   SCASB           ;Scan for end of path string
  635.         MOV     BYTE PTR [DI-1],"\" ;Add the trailing backslash
  636. STORE_PATH:
  637.         PUSH    DI              ;Save location to append path
  638.         MOV     AL," "          ;Look for blank spaces
  639.         CALL    LOAD_PARAMS
  640.         REPE    SCASB           ;Scan for non-blank character
  641.         MOV     SI,DI
  642.         DEC     SI              ;This is first letter of path
  643.         POP     DI              ;Get back location to append
  644. COPY_PATH:
  645.         LODSB                   ;Get next char of path
  646.         CMP     AL," "          ;Is it a blank?
  647.         JE      VERIFY_NAME     ;If yes, its the last char
  648.         CMP     AL,13           ;Is it a carriage return?
  649.         JE      VERIFY_NAME     ;If yes, its the last char
  650.         STOSB                   ;Store this letter
  651.         JMP     COPY_PATH       ;Copy until end of path found
  652. VERIFY_NAME:
  653.         PUSH    DI              ;Save end of string location
  654.         MOV     BYTE PTR [DI],"$" ;Mark eos for dos display
  655.         MOV     DX,OFFSET REDIRECT_MESS ;Point to message
  656.         MOV     AH,9            ;Display the string of text
  657.         INT     21H             ;Using dos display function
  658.         MOV     DX,OFFSET FILENAME ;Point to filename for display
  659.         CALL    STRING_CRLF     ;Display the string
  660.         POP     DI
  661.         MOV     BYTE PTR [DI],0 ;Now make it an ascii string
  662.         MOV     DX,OFFSET FILENAME ;Dx points to the filename
  663.  
  664.                 CMP  CS:APPEND_FILE,1   ;Append file turned on?
  665.                 JNE  OPEN_ERR           ;If not, take jump
  666.  
  667.         MOV     AX,3D00H        ;Open this file for reading
  668.         INT     21H
  669.         JC      OPEN_ERR        ;Error may indicate not found
  670. CLOSE_IT:
  671.         MOV     BX,AX           ;Get the handle into BX
  672.         MOV     AH,3EH          ;Close the file
  673.         INT     21H
  674.         JMP     FILENAME_OK
  675. OPEN_ERR:
  676.         MOV     CX,0020H        ;Attribute for new file
  677.         MOV     AH,3CH          ;Create file for writing
  678.         INT     21H             ;Dos function to create file
  679.         JNC     CLOSE_IT        ;If no error, just close it
  680. BAD_NAME_EXIT:
  681.         MOV     DX,OFFSET BAD_FILENAME
  682. ERR_EXIT:
  683.         CALL    STRING_CRLF     ;Display the string
  684.         INT     20H             ;Just exit to dos
  685. FILENAME_OK:
  686.         MOV     ES,INSTALLED_SEG;Point to installed program
  687.         PUSH    DS:PRINTER_NUM  ;This moves the new printer
  688.         POP     ES:PRINTER_NUM  ;number to the resident copy
  689.         MOV     DI,OFFSET FILENAME ;Setup to copy the filename
  690.         MOV     SI,DI
  691.         MOV     CX,128          ;Copy entire file specification
  692.         REP     MOVSB           ;String move instruction
  693.         MOV     AL,1            ;Turn redirection on
  694. CHECK_FOR_INSTALL:
  695.         MOV     CX,CS
  696.         CMP     CX,INSTALLED_SEG
  697.         MOV     ES,INSTALLED_SEG
  698.         MOV     ES:SWITCH,AL    ;Store the new on/off switch
  699.         JE      INSTALL         ;If not installed yet, do it now
  700.         INT     20H             ;Otherwise terminate
  701.  
  702. ;----------------------------------------------------------------------
  703. ; This subroutine displays a string followed by a CR and LF
  704. ;----------------------------------------------------------------------
  705. STRING_CRLF     PROC    NEAR
  706.  
  707.         MOV     AH,9            ;Display the string of text
  708.         INT     21H             ;Using dos display function
  709.         MOV     DX,OFFSET CRLF  ;Now point to CR/LF characters
  710.         MOV     AH,9            ;Send the CR and LF
  711.         INT     21H
  712.         RET
  713.  
  714. STRING_CRLF     ENDP
  715.  
  716. ;----------------------------------------------------------------------
  717. ; This subroutine sets DI to the command line and CX to the byte count
  718. ;----------------------------------------------------------------------
  719. LOAD_PARAMS     PROC    NEAR
  720.  
  721.         MOV     DI,80H          ;Point to parameter area
  722.         MOV     CL,CS:[DI]      ;Get number of chars into CL
  723.         XOR     CH,CH           ;Make it a word
  724.         INC     DI              ;Point to first character
  725.         CLD                     ;String search forward
  726.         RET
  727.  
  728. LOAD_PARAMS     ENDP
  729.  
  730. ;----------------------------------------------------------------------
  731. ; This code does the actual installation by storing the existing
  732. ; interrupt vectors and replacing them with the new ones.
  733. ; Then allocate memory for the buffer.  Exit and remain resident.
  734. ;----------------------------------------------------------------------
  735.         ASSUME  DS:CSEG, ES:CSEG
  736. INSTALL:
  737.         MOV     BX,OFFSET END_OF_CODE   ;Get end of resident code
  738.         ADD     BX,15
  739.         MOV     CL,4            ;Shift by 4 to divide by 16
  740.         SHR     BX,CL           ;This converts to paragraphs
  741.         MOV     AH,4AH          ;Modify memory block
  742.         INT     21H             ;Dos setblock function call
  743.         JNC     ALLOCATE_BUFFER ;If it worked ok, then continue
  744. ALLOC_ERROR:
  745.         MOV     DX,OFFSET BAD_ALLOC ;Err message for bad allocation
  746.         JMP     ERR_EXIT        ;Display message and exit
  747. ALLOCATE_BUFFER:
  748.         MOV     BX,BUFF_SIZE    ;Buffer size in K bytes
  749.         MOV     CL,6            ;Shift by 6 to get paragraphs
  750.         SHL     BX,CL           ;Buffersize is in paragraphs
  751.         MOV     AH,48H
  752.         INT     21H             ;Dos allocate memory
  753.         JC      ALLOC_ERROR     ;If allocation error, take jump
  754.         MOV     BUFF_SEGMENT,AX ;Save the segment for the buffer
  755.  
  756.         MOV     AX,BUFF_SIZE    ;Buffer size in K bytes
  757.         MOV     CL,10           ;Shift by 10 to get bytes
  758.         SHL     AX,CL
  759.         OR      AX,AX           ;Is buff_size=0 (64K)?
  760.         JNZ     SIZE_OK
  761.         DEC     AX              ;If yes, make it FFFFh
  762. SIZE_OK:
  763.         MOV     BUFF_SIZE,AX    ;Now buff_size is in bytes
  764.  
  765.         ASSUME  ES:NOTHING
  766.  
  767.         MOV     AH,34H          ;Get dos busy flag location
  768.         INT     21H
  769.         MOV     WORD PTR [DOS_FLAG]  ,BX ;Store flag address
  770.         MOV     WORD PTR [DOS_FLAG+2],ES
  771.  
  772.         MOV     AX,3508H        ;Get timer interrupt vector
  773.         INT     21H
  774.         MOV     WORD PTR [OLDINT08]  ,BX
  775.         MOV     WORD PTR [OLDINT08+2],ES
  776.         MOV     DX, OFFSET NEWINT08
  777.         MOV     AX, 2508H
  778.         INT     21H             ;Dos function to change vector
  779.  
  780.         MOV     AX,3517H        ;Get printer interrupt vector
  781.         INT     21H
  782.         MOV     WORD PTR [OLDINT17]  ,BX
  783.         MOV     WORD PTR [OLDINT17+2],ES
  784.         MOV     DX, OFFSET NEWINT17
  785.         MOV     AX, 2517H
  786.         INT     21H             ;Dos function to change vector
  787.  
  788.         MOV     AX,3521H        ;Get dos function vector
  789.         INT     21H
  790.         MOV     WORD PTR [OLDINT21]  ,BX
  791.         MOV     WORD PTR [OLDINT21+2],ES
  792.         MOV     DX, OFFSET NEWINT21
  793.         MOV     AX, 2521H
  794.         INT     21H             ;Dos function to change vector
  795.  
  796.         MOV     AX,3528H        ;Get dos waiting vector
  797.         INT     21H
  798.         MOV     WORD PTR [OLDINT28]  ,BX
  799.         MOV     WORD PTR [OLDINT28+2],ES
  800.         MOV     DX, OFFSET NEWINT28
  801.         MOV     AX, 2528H
  802.         INT     21H             ;Dos function to change vector
  803.  
  804. ;----------------------------------------------------------------------
  805. ; Deallocate our copy of the enviornment.  Exit using interrupt 27h
  806. ; (TSR). This leaves code and space for buffer resident.
  807. ;----------------------------------------------------------------------
  808.  
  809.         MOV     AX,DS:[002CH]   ;Get segment of enviornment
  810.         MOV     ES,AX           ;Put it into ES
  811.         MOV     AH,49H          ;Release allocated memory
  812.         INT     21H
  813.         MOV     DX,(OFFSET END_OF_CODE - OFFSET CSEG + 15)SHR 4
  814.         MOV     AX,3100H
  815.         INT     21H             ;Terminate and stay resident
  816.  
  817. ;----------------------------------------------------------------------
  818. ; This procedure removes PRN2FILE from memory by replacing the vectors
  819. ; and releasing the memory used for the code and buffer.
  820. ;----------------------------------------------------------------------
  821.         ASSUME  DS:CSEG, ES:NOTHING
  822. UNINSTALL:
  823.         MOV     AL,08H          ;Check the timer interrupt
  824.         CALL    CHECK_SEG       ;If changed, can't uninstall
  825.         JNE     CANT_UNINSTALL
  826.  
  827.         MOV     AL,17H          ;Check the printer interrupt
  828.         CALL    CHECK_SEG       ;If changed, can't uninstall
  829.         JNE     CANT_UNINSTALL
  830.  
  831.         MOV     AL,21H          ;Check dos interrupt
  832.         CALL    CHECK_SEG       ;If changed, can't uninstall
  833.         JNE     CANT_UNINSTALL
  834.  
  835.         MOV     AL,28H          ;Check dos idle interrupt
  836.         CALL    CHECK_SEG       ;If changed, can't uninstall
  837.         JNE     CANT_UNINSTALL
  838.  
  839.         MOV     ES,INSTALLED_SEG
  840.         ASSUME  DS:NOTHING, ES:NOTHING
  841.  
  842.         LDS     DX,ES:OLDINT08  ;Get original vector
  843.         MOV     AX,2508H
  844.         INT     21H             ;Dos function to change vector
  845.  
  846.         LDS     DX,ES:OLDINT17  ;Get original vector
  847.         MOV     AX,2517H
  848.         INT     21H             ;Dos function to change vector
  849.  
  850.         LDS     DX,ES:OLDINT21  ;Get original vector
  851.         MOV     AX,2521H
  852.         INT     21H             ;Dos function to change vector
  853.  
  854.         LDS     DX,ES:OLDINT28  ;Get original vector
  855.         MOV     AX,2528H
  856.         INT     21H             ;Dos function to change vector
  857.  
  858.         MOV     ES,ES:BUFF_SEGMENT;Get segment of buffer
  859.         MOV     AH,49H          ;Free its allocated memory
  860.         INT     21H
  861.         JC      RELEASE_ERR     ;If error, take jump
  862.  
  863.         MOV     ES,INSTALLED_SEG;The resident program segment
  864.         NOT     WORD PTR ES:START
  865.         MOV     AH,49H          ;Free its allocated memory
  866.         INT     21H
  867.         JC      RELEASE_ERR     ;If error, take jump
  868.         MOV     AX,4C00H
  869.         INT     21H             ;Exit to dos
  870. RELEASE_ERR:
  871.         MOV     DX,OFFSET BAD_ALLOC ;Memory allocation error
  872.         JMP     ERR_EXIT        ;Exit with error message
  873. CANT_UNINSTALL:
  874.         MOV     DX,OFFSET BAD_UNINSTALL ;Point to error message
  875.         JMP     ERR_EXIT        ;Exit with error message
  876.  
  877. ;----------------------------------------------------------------------
  878. ; This subroutine checks to see if an interrupt vector points to the
  879. ; installed program segment. Returns with ZF=1 if it does.
  880. ;----------------------------------------------------------------------
  881. CHECK_SEG       PROC    NEAR
  882.  
  883.         MOV     AH,35H          ;Get the vector
  884.         INT     21H             ;Dos function to get the vector
  885.         MOV     AX,ES
  886.         CMP     AX,INSTALLED_SEG;Is it the installed segment?
  887.         RET
  888.  
  889. CHECK_SEG       ENDP
  890. ;----------------------------------------------------------------------
  891. FILENAME        LABEL   BYTE            ;File name will go here
  892. END_OF_CODE     =       $ + 128         ;Allow 128 bytes for it
  893.  
  894. CSEG            ENDS
  895.         END     START
  896.